//: C21:FunctionObjects.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
// Using the predefined function object templates
// in the Standard C++ library
// This will be defined shortly:
#include "Generators.h"
#include <algorithm>
#include <vector>
#include <iostream>
#include <functional>
using namespace std;

template<typename T>
void print(vector<T>& v, char* msg = "")
{
    if (*msg != 0)
        cout << msg << ":" << endl;
    copy(v.begin(), v.end(),
         ostream_iterator<T>(cout, " "));
    cout << endl;
}

template<typename Contain, typename UnaryFunc>
void testUnary(Contain& source, Contain& dest,
               UnaryFunc f)
{
    transform(source.begin(), source.end(),
              dest.begin(), f);
}

template<typename Contain1, typename Contain2,
typename BinaryFunc>
void testBinary(Contain1& src1, Contain1& src2,
                Contain2& dest, BinaryFunc f)
{
    transform(src1.begin(), src1.end(),
              src2.begin(), dest.begin(), f);
}

// Executes the expression, then stringizes the
// expression into the print statement:
#define T(EXPR) EXPR; print(r, "After " #EXPR);
// For Boolean tests:
#define B(EXPR) EXPR; print(br,"After " #EXPR);

// Boolean random generator:
struct BRand {
    BRand() {
        srand(time(0));
    }
    bool operator()() {
        return rand() > RAND_MAX / 2;
    }
};

int main()
{
    const int sz = 10;
    const int max = 50;
    vector<int> x(sz), y(sz), r(sz);
    // An integer random number generator:
    URandGen urg(max);
    generate_n(x.begin(), sz, urg);
    generate_n(y.begin(), sz, urg);
    // Add one to each to guarantee nonzero divide:
    transform(y.begin(), y.end(), y.begin(),
              bind2nd(plus<int>(), 1));
    // Guarantee one pair of elements is ==:
    x[0] = y[0];
    print(x, "x");
    print(y, "y");
    // Operate on each element pair of x & y,
    // putting the result into r:
    T(testBinary(x, y, r, plus<int>()));
    T(testBinary(x, y, r, minus<int>()));
    T(testBinary(x, y, r, multiplies<int>()));
    T(testBinary(x, y, r, divides<int>()));
    T(testBinary(x, y, r, modulus<int>()));
    T(testUnary(x, r, negate<int>()));
    vector<bool> br(sz); // For Boolean results
    B(testBinary(x, y, br, equal_to<int>()));
    B(testBinary(x, y, br, not_equal_to<int>()));
    B(testBinary(x, y, br, greater<int>()));
    B(testBinary(x, y, br, less<int>()));
    B(testBinary(x, y, br, greater_equal<int>()));
    B(testBinary(x, y, br, less_equal<int>()));
    B(testBinary(x, y, br,
                 not2(greater_equal<int>())));
    B(testBinary(x,y,br,not2(less_equal<int>())));
    vector<bool> b1(sz), b2(sz);
    generate_n(b1.begin(), sz, BRand());
    generate_n(b2.begin(), sz, BRand());
    print(b1, "b1");
    print(b2, "b2");
    B(testBinary(b1, b2, br, logical_and<int>()));
    B(testBinary(b1, b2, br, logical_or<int>()));
    B(testUnary(b1, br, logical_not<int>()));
    B(testUnary(b1, br, not1(logical_not<int>())));
} ///:~
